package com.example.blog.modules.system.service.impl;

import com.example.blog.common.utils.ExportUtils;
import com.example.blog.modules.article.entity.Article;
import com.example.blog.modules.article.repository.ArticleRepository;
import com.example.blog.modules.article.repository.CategoryRepository;
import com.example.blog.modules.article.repository.CommentRepository;
import com.example.blog.modules.article.repository.TagRepository;
import com.example.blog.modules.system.dto.DashboardDTO;
import com.example.blog.modules.system.dto.CategoryStatDTO;
import com.example.blog.modules.system.dto.TagStatDTO;
import com.example.blog.modules.system.repository.NotificationRepository;
import com.example.blog.modules.system.service.DashboardService;
import com.example.blog.modules.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.core.io.Resource;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

@Service
@RequiredArgsConstructor
public class DashboardServiceImpl implements DashboardService {

    private final UserRepository userRepository;
    private final ArticleRepository articleRepository;
    private final CategoryRepository categoryRepository;
    private final TagRepository tagRepository;
    private final CommentRepository commentRepository;
    private final NotificationRepository notificationRepository;

    @Override
    @Transactional(readOnly = true)
    @Cacheable(value = "dashboard", key = "'stats'")
    public DashboardDTO getDashboardData() {
        DashboardDTO dto = new DashboardDTO();
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startOfToday = now.toLocalDate().atStartOfDay();
        LocalDateTime startOfWeek = now.minusDays(now.getDayOfWeek().getValue() - 1).toLocalDate().atStartOfDay();
        LocalDateTime startOfMonth = now.withDayOfMonth(1).toLocalDate().atStartOfDay();

        // 用户统计
        dto.setTotalUsers(userRepository.count());
        dto.setActiveUsers(userRepository.countByLastLoginTimeAfter(startOfMonth));
        dto.setNewUsersToday(userRepository.countByCreatedAtAfter(startOfToday));
        dto.setNewUsersThisWeek(userRepository.countByCreatedAtAfter(startOfWeek));
        dto.setNewUsersThisMonth(userRepository.countByCreatedAtAfter(startOfMonth));

        // 文章统计
        dto.setTotalArticles(articleRepository.count());
        dto.setPublishedArticles(articleRepository.countByIsPublishedTrue());
        dto.setDraftArticles(articleRepository.countByIsPublishedFalse());
        dto.setNewArticlesToday(articleRepository.countByCreatedAtAfter(startOfToday));
        dto.setNewArticlesThisWeek(articleRepository.countByCreatedAtAfter(startOfWeek));
        dto.setNewArticlesThisMonth(articleRepository.countByCreatedAtAfter(startOfMonth));
        dto.setTotalViews(calculateTotalViews());
        dto.setTotalLikes(calculateTotalLikes());
        dto.setTotalComments(commentRepository.count());

        // 分类统计
        dto.setTotalCategories(categoryRepository.count());
        dto.setTopCategories(getTopCategories());

        // 标签统计
        dto.setTotalTags(tagRepository.count());
        dto.setTopTags(getTopTags());

        // 评论统计
        dto.setPendingComments(commentRepository.countByIsApprovedFalse());
        dto.setApprovedComments(commentRepository.countByIsApprovedTrue());
        dto.setNewCommentsToday(commentRepository.countByCreatedAtAfter(startOfToday));
        dto.setNewCommentsThisWeek(commentRepository.countByCreatedAtAfter(startOfWeek));

        // 趋势数据
        dto.setUserTrend(getUserTrend());
        dto.setArticleTrend(getArticleTrend());
        dto.setViewTrend(getViewTrend());
        dto.setCommentTrend(getCommentTrend());

        return dto;
    }

    @Override
    @Transactional(readOnly = true)
    @Cacheable(value = "dashboard", key = "'data'")
    public DashboardDTO getDashboardData(LocalDate startDate, LocalDate endDate) {
        DashboardDTO dto = new DashboardDTO();
        LocalDateTime start = startDate.atStartOfDay();
        LocalDateTime end = endDate.atTime(LocalTime.MAX);
        
        // 用户统计
        dto.setTotalUsers(userRepository.count());
        dto.setActiveUsers(userRepository.countByLastLoginTimeBetween(start, end));
        dto.setNewUsersToday(userRepository.countByCreatedAtBetween(start, end));
        
        // 文章统计
        dto.setTotalArticles(articleRepository.count());
        dto.setPublishedArticles(articleRepository.countByStatusAndCreatedAtBetween("PUBLISHED", start, end));
        dto.setDraftArticles(articleRepository.countByStatusAndCreatedAtBetween("DRAFT", start, end));
        dto.setTotalViews(articleRepository.sumViewsByUpdateTimeBetween(start, end));
        dto.setTotalLikes(articleRepository.sumLikesByUpdateTimeBetween(start, end));
        dto.setTotalComments(commentRepository.countByCreatedAtBetween(start, end));
        
        return dto;
    }

    private Long calculateTotalViews() {
        return articleRepository.findAll().stream()
                .mapToLong(Article::getViewCount)
                .sum();
    }

    private Long calculateTotalLikes() {
        return articleRepository.findAll().stream()
                .mapToLong(Article::getLikeCount)
                .sum();
    }

    private List<CategoryStatDTO> getTopCategories() {
        return categoryRepository.findTop5Categories();
    }

    private List<TagStatDTO> getTopTags() {
        return tagRepository.findTop5Tags();
    }

    private Map<String, Long> getUserTrend() {
        LocalDateTime endDate = LocalDateTime.now();
        LocalDateTime startDate = endDate.minus(30, ChronoUnit.DAYS);
        return userRepository.findDailyNewUsers(startDate, endDate);
    }

    private Map<String, Long> getArticleTrend() {
        LocalDateTime endDate = LocalDateTime.now();
        LocalDateTime startDate = endDate.minus(30, ChronoUnit.DAYS);
        return articleRepository.findDailyNewArticles(startDate, endDate);
    }

    private Map<String, Long> getViewTrend() {
        LocalDateTime endDate = LocalDateTime.now();
        LocalDateTime startDate = endDate.minus(30, ChronoUnit.DAYS);
        return articleRepository.findDailyViews(startDate, endDate);
    }

    private Map<String, Long> getCommentTrend() {
        LocalDateTime endDate = LocalDateTime.now();
        LocalDateTime startDate = endDate.minus(30, ChronoUnit.DAYS);
        return commentRepository.findDailyNewComments(startDate, endDate);
    }

    @Override
    @Transactional(readOnly = true)
    @Cacheable(value = "dashboard", key = "'trend:' + #days + ':' + #startDate")
    public DashboardDTO getTrendData(int days, LocalDate startDate) {
        DashboardDTO dto = new DashboardDTO();
        LocalDateTime start = startDate.atStartOfDay();
        LocalDateTime end = start.plusDays(days).with(LocalTime.MAX);
        
        Map<String, Long> userTrend = new LinkedHashMap<>();
        Map<String, Long> articleTrend = new LinkedHashMap<>();
        Map<String, Long> viewTrend = new LinkedHashMap<>();
        Map<String, Long> commentTrend = new LinkedHashMap<>();
        
        for (int i = 0; i < days; i++) {
            LocalDateTime currentDate = start.plusDays(i);
            String dateStr = currentDate.toLocalDate().toString();
            LocalDateTime nextDate = currentDate.plusDays(1);
            
            userTrend.put(dateStr, userRepository.countByCreateTimeBetween(currentDate, nextDate));
            articleTrend.put(dateStr, articleRepository.countByCreateTimeBetween(currentDate, nextDate));
            viewTrend.put(dateStr, articleRepository.sumViewsByUpdateTimeBetween(currentDate, nextDate));
            commentTrend.put(dateStr, commentRepository.countByCreateTimeBetween(currentDate, nextDate));
        }
        
        dto.setUserTrend(userTrend);
        dto.setArticleTrend(articleTrend);
        dto.setViewTrend(viewTrend);
        dto.setCommentTrend(commentTrend);
        
        return dto;
    }

    @Override
    public Resource exportDashboardData(LocalDate startDate, LocalDate endDate, String format) {
        DashboardDTO data = getDashboardData(startDate, endDate);
        
        List<String> headers = Arrays.asList(
            "指标", "数值"
        );
        
        List<List<Object>> rows = new ArrayList<>();
        rows.add(Arrays.asList("总用户数", data.getTotalUsers()));
        rows.add(Arrays.asList("活跃用户数", data.getActiveUsers()));
        rows.add(Arrays.asList("今日新增用户", data.getNewUsersToday()));
        rows.add(Arrays.asList("本周新增用户", data.getNewUsersThisWeek()));
        rows.add(Arrays.asList("本月新增用户", data.getNewUsersThisMonth()));
        rows.add(Arrays.asList("总文章数", data.getTotalArticles()));
        rows.add(Arrays.asList("已发布文章", data.getPublishedArticles()));
        rows.add(Arrays.asList("草稿文章", data.getDraftArticles()));
        rows.add(Arrays.asList("总浏览量", data.getTotalViews()));
        rows.add(Arrays.asList("总点赞数", data.getTotalLikes()));
        rows.add(Arrays.asList("总评论数", data.getTotalComments()));
        rows.add(Arrays.asList("待审核评论", data.getPendingComments()));
        rows.add(Arrays.asList("已通过评论", data.getApprovedComments()));
        rows.add(Arrays.asList("总通知数", data.getTotalNotifications()));
        rows.add(Arrays.asList("未读通知", data.getUnreadNotifications()));
        rows.add(Arrays.asList("系统通知", data.getSystemNotifications()));
        
        return switch (format.toLowerCase()) {
            case "excel" -> ExportUtils.exportToExcel("仪表盘数据", headers, rows);
            case "csv" -> ExportUtils.exportToCsv(headers, rows);
            case "json" -> ExportUtils.exportToJson(data);
            default -> throw new IllegalArgumentException("不支持的导出格式：" + format);
        };
    }

    @Override
    public Resource exportTrendData(int days, LocalDate startDate, String format) {
        DashboardDTO data = getTrendData(days, startDate);
        
        List<String> headers = Arrays.asList(
            "日期", "新增用户", "新增文章", "浏览量", "新增评论"
        );
        
        List<List<Object>> rows = new ArrayList<>();
        Set<String> dates = new TreeSet<>(data.getUserTrend().keySet());
        
        for (String date : dates) {
            rows.add(Arrays.asList(
                date,
                data.getUserTrend().get(date),
                data.getArticleTrend().get(date),
                data.getViewTrend().get(date),
                data.getCommentTrend().get(date)
            ));
        }
        
        return switch (format.toLowerCase()) {
            case "excel" -> ExportUtils.exportToExcel("趋势数据", headers, rows);
            case "csv" -> ExportUtils.exportToCsv(headers, rows);
            case "json" -> ExportUtils.exportToJson(data);
            default -> throw new IllegalArgumentException("不支持的导出格式：" + format);
        };
    }
} 